/*
 * Copyright (C) 2012, EADS France
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.frontends;

/**
 *
 * @author Laurent WOUTERS
 */
public class RewindableStream {
    private static final int bufferInitSize = 1024;
    
    private java.io.Reader reader;
    private char[] buffer;      // First stage buffer for reading batch reading of the stream
    private int bufferStart;    // Index where the next character shall be read in the buffer
    private int bufferSize;     // Current size of the buffer
    private char[] ring;        // Ring memory of this reader storing the already read characters
    private int ringStart;      // Start index of the ring in case the stream in rewinded
    private int ringNextEntry;  // Index for inserting new characters in the ring

    public RewindableStream(java.io.Reader reader)
    {
        this.reader = reader;
        this.buffer = new char[bufferInitSize];
        this.bufferStart = 0;
        this.bufferSize = 0;
        this.ring = new char[bufferInitSize];
        this.ringStart = 0;
        this.ringNextEntry = 0;
    }

    private char readRing()
    {
        char value = ring[ringStart];
        ringStart++;
        if (ringStart == bufferInitSize)
            ringStart = 0;
        return value;
    }

    private void pushInRing(char value)
    {
        ring[ringNextEntry++] = value;
        if (ringNextEntry == bufferInitSize)
            ringNextEntry = 0;
        ringStart = ringNextEntry;
    }

    public void rewind(int count)
    {
        ringStart -= count;
        if (ringStart < 0)
            ringStart += bufferInitSize;
    }

    public boolean isAtEnd() {
        if (ringStart != ringNextEntry)
            return false;
        if (bufferStart != bufferSize)
            return false;
        loadBuffer();
        return (bufferSize == 0);
    }
    
    private void loadBuffer() {
        try { bufferSize = reader.read(buffer, 0, buffer.length); }
        catch (java.lang.Exception ex) { bufferSize = 0; }
        bufferStart = 0;
    }
    
    public char read()
    {
        if (ringStart != ringNextEntry)
            return readRing();
        char c = buffer[bufferStart++];
        pushInRing(c);
        return c;
    }
}
